# 트랜잭션과 쿼리 실행하기


# 연결 얻기

다음처럼 데이터베이스에 연결하여 쿼리를 실행할 수 있습니다.

>>> from sqlalchemy import text

>>> with engine.connect() as conn:
...     result = conn.execute(text("select 'hello world'"))
...     print(result.all())

[('hello world',)]
  • engine.connect() 으로 Connection (opens new window) 객체를 얻어 conn 에 담습니다.
    • Connection 객체를 통해 데이터베이스와 상호작용할 수 있습니다.
    • with ... 구문은 하나의 트랜잭션 단위가 됩니다.
  • 트랜잭션은 자동으로 커밋되지 않습니다.
    • Connection.commit() 을 호출해야 커밋됩니다.

# 변경 사항 커밋하기

연결을 얻고, 트랜잭션을 연 뒤 데이터베이스와 상호작용하는 일들은 자동으로 커밋되지 않습니다.
커밋을 하려면 다음처럼 Connection.commit() 을 호출해야 합니다.

>>> with engine.connect() as conn:
...     # 테이블을 생성합니다.
...     conn.execute(text("CREATE TABLE some_table (x int, y int)")) 
...     # 데이터를 삽입합니다.
...     conn.execute(
...         text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
...         [{"x": 1, "y": 1}, {"x": 2, "y": 4}]
...     )
...     # 위 변경사항을 커밋합니다.
...     conn.commit()

위 코드의 실행하면 다음과 같은 결과가 출력됩니다.

BEGIN (implicit)
CREATE TABLE some_table (x int, y int)
[...] ()
<sqlalchemy.engine.cursor.CursorResult object at 0x...>
INSERT INTO some_table (x, y) VALUES (?, ?)
[...] ((1, 1), (2, 4))
<sqlalchemy.engine.cursor.CursorResult object at 0x...>
COMMIT

Engine.begin() 으로 트랜잭션 종료시 자동으로 커밋을 찍게할 수도 있습니다.

>>> with engine.begin() as conn:
...     conn.execute(
...         text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
...         [{"x": 6, "y": 8}, {"x": 9, "y": 10}]
...     )
... # 트랜잭션 (컨텍스트 구문)이 끝나면 커밋됩니다.

위 코드의 실행하면 다음과 같은 결과가 출력됩니다.

BEGIN (implicit)
INSERT INTO some_table (x, y) VALUES (?, ?)
[...] ((6, 8), (9, 10))
<sqlalchemy.engine.cursor.CursorResult object at 0x...>
COMMIT

# 명령문 실행의 기초

다음처럼 쿼리를 실행하고 그 결과를 받아올 수 있습니다.

>>> with engine.connect() as conn:
...     # conn.execute() 는 Result라는 객체에 내보냅니다.
...     result = conn.execute(text("SELECT x, y FROM some_table"))
...     for row in result:
...         print(f"x: {row.x}  y: {row.y}")

x: 1  y: 1
x: 2  y: 4
x: 6  y: 8
x: 9  y: 10
  • Result (opens new window) 객체는 conn.execute() 가 반환해주는 "쿼리 결과"를 들고 있는 객체입니다.
    • 링크를 눌러보면 어떤 기능을 제공하는지 볼 수 있습니다.
    • 예를 들면 Result.all() 을 통해 Row (opens new window) 객체의 리스트를 받을 수 있습니다.

ResultRow 모두 sqlalchemy에서 제공하는 객체입니다.

Result 객체로 다음처럼 각 행에 액세스할 수 있습니다.

result = conn.execute(text("select x, y from some_table"))

# 튜플로 접근합니다.
for x, y in result:
    # ...
    
# 정수 인덱스로 접근합니다.
for row in result:
    x = row[0]

# 속성 이름으로 접근합니다.
for row in result:
    y = row.y
    
# 매핑 액세스로 접근합니다.
for dict_row in result.mappings():
    x = dict_row['x']
    y = dict_row['y']

# 쿼리에 매개 변수 전달하기

쿼리에 다음처럼 파라미터를 전달할 수 있습니다.

>>> with engine.connect() as conn:
...     result = conn.execute(
...         text("SELECT x, y FROM some_table WHERE y > :y"),  # 콜론 형식(:)으로 받습니다.
...         {"y": 2}  # 사전 형식으로 넘깁니다.
...     )
...     for row in result:
...        print(f"x: {row.x}  y: {row.y}")

x: 2  y: 4
x: 6  y: 8
x: 9  y: 10

다음처럼 여러 개의 매개 변수를 보낼수도 있습니다.

>>> with engine.connect() as conn:
...     conn.execute(
...         text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
...         [{"x": 11, "y": 12}, {"x": 13, "y": 14}]  # 사전의 리스트로 넘깁니다.
...     )
...     conn.commit()

위 코드는 다음과 같은 쿼리를 실행하게 됩니다.

INSERT INTO some_table (x, y) VALUES (?, ?) [...] ((11, 12), (13, 14))

"명령문으로 매개 변수 묶기" (opens new window) 가 공식 문서에 나오지만, 이 부분은 저도 이해가 되지 않아 넘기겠습니다.
추후 이해하신 분은 이 문서에 기여해주시면 감사하겠습니다.


# ORM Session으로 실행

이번에는 Connection 객체가 아니라 ORM에서 제공해주는 Session 로 쿼리를 실행해봅시다.
다음처럼 해볼 수 있습니다.

>>> from sqlalchemy.orm import Session

>>> stmt = text("SELECT x, y FROM some_table WHERE y > :y ORDER BY x, y").bindparams(y=6)

>>> # Session 객체에 Engine 객체의 인스턴스를 넘겨 데이터베이스와 상호작용할 수 있는 인스턴스를 얻습니다.
>>> with Session(engine) as session:
...     # Session.execute() 메서드로 쿼리를 실행합니다.
...     result = session.execute(stmt)
...     for row in result:
...        print(f"x: {row.x}  y: {row.y}")

Session 역시 종료 시에 자동으로 커밋을하지 않습니다. 커밋을 하려면 다음처럼 직접 Session.commit() 을 호출해야 합니다.

>>> with Session(engine) as session:
...     result = session.execute(
...         text("UPDATE some_table SET y=:y WHERE x=:x"),
...         [{"x": 9, "y":11}, {"x": 13, "y": 15}]
...     )
...     session.commit()  # 명시적으로 호출해야 합니다.